//
//  DemoClosure.swift
//  swiftDemo
//
//  Created by JIENING ZHANG on 2022/11/21.
//  Copyright © 2022 lovivid. All rights reserved.
//

import UIKit

class DemoClosure: NSObject {
    
    class func closureAsFuncReturn(_ base:Int) -> (Int) -> Int {
        var sum = base;
        return {addNum in
            // sum 被捕获 存放在 堆内存区，所以当 闭包被当做返回值 返回的时候，sum的值等于 base
            sum += addNum;
            return sum;
        }
    }
    
    class func coordTranAction(_ coord:[Float],
                                     _ transArg:Any?,
                                     _ transLogic: ([Float], Any?) -> [Float]? ) -> [Float]? {
        if coord.count < 2 {
            return nil;
        }
        return transLogic(coord, transArg);
    }
    
    class func sortedClosureShow() {
        // https://juejin.cn/post/6972560642427486238
        
        let numbers = [1,9,2,8,3,7,4,6];
        
        // 传递函数
        let numArr = numbers.sorted(by: callback)
        func callback(_ numA:Int, _ numB:Int) -> Bool {
            return numA < numB;
        }
        print(numArr);

        // 使用闭包代替函数
        let numArr1 = numbers.sorted(by: { (_ numA:Int, _ numB:Int) -> Bool in
            return numA < numB
        });
        print(numArr1);
        
        // 尾随闭包1：如果闭包是函数的最后一个参数，那么调用的时候将闭包写在括号()后面
        let numArr2 = numbers.sorted() { (_ numA:Int, _ numB:Int) -> Bool in
            return numA < numB
        }
        print(numArr2);
        
        // 尾随闭包2：如果函数只有一个参数且为闭包，那么调用的时候括号()可以不写
        let numArr3 = numbers.sorted { (_ numA:Int, _ numB:Int) -> Bool in
            return numA < numB
        }
        print(numArr3);
        
        // 根据上下文推断类型:闭包入参所有的类型都可以被正确推断，所以返回箭头（->）和围绕在参数周围的括号也可以被省略
        let numArr4 = numbers.sorted { numA, numB in
            return numA < numB
        }
        print(numArr4);
        
        // 单表达式闭包隐式返回:单一表达式（s1 > s2)，该表达式返回Bool类型值，因此这里没有歧义，return关键字可以省略
        let numArr5 = numbers.sorted { numA, numB in numA < numB}
        print(numArr5);
        
        
        // 参数名称缩写:可以直接通过$0,$1,$2 来顺序调用闭包的参数，以此类推。
        //     如果你在闭包表达式中使用参数名称缩写，你可以在闭包定义中省略参数列表，并且对应参数名称缩写的类型会通过函数类型进行推断。
        //     in关键字也同样可以被省略，因为此时闭包表达式完全由闭包函数体构成
        let numArr6 = numbers.sorted {$0 < $1}
        print(numArr6);
        
        // 运算符方法:swift 的 Int类型定义了关于大于号（>）的实现，其作为一个函数接受两个Int类型的参数并返回Bool类型的值
        let numArr7 = numbers.sorted(by: <);
        print(numArr7);
    }
    
    
    class func closureAsFuncInputArg() {
        
        // 平面坐标 转 极坐标
        // 闭包 赋值
        let polarCoordTrans = { (coord:[Float], transArg:Any?) -> [Float]? in
            if coord.count < 2 {
                return nil;
            }
            var rtnCoord:[Float] = [0, 0];
            rtnCoord[0] = sqrtf(coord[0]*coord[0] + coord[1]*coord[1]);
            if (0 == rtnCoord[0]) {
                rtnCoord[1] = 0;
            } else {
                // 极坐标 角度 1 2 象限
                rtnCoord[1] = acosf(coord[0]/rtnCoord[0]);
                if (rtnCoord[0] < 0) {
                    // 3 4 象限
                    rtnCoord[1] = .pi - rtnCoord[1];
                }
            }
            return rtnCoord;
        }
        
        // 闭包做函数入参
        var input:[Float] = [1, 2];
        var transCoord = coordTranAction(input, nil, polarCoordTrans);
        print("笛卡尔坐标 \(input) 转 极坐标 \(transCoord ?? [])");
        
        let rotateCoordTrans = { (coord:[Float], transArg:Any?) -> [Float]? in
            if coord.count < 2 {
                return nil;
            }
            guard transArg is Float else {
                return nil;
            }
            let angle = transArg as! Float;
            
            var rtn:[Float] = [0, 0];
            rtn[0] = coord[0] * cosf(angle) + coord[1] * sinf(angle); // 变量捕获 外部的 angle编译不过
            rtn[1] = coord[1] * cosf(angle) - coord[0] * sinf(angle); // angle 只能当做参数传递进来
            return rtn;
        }
        
        var angle:Float = .pi/6;
        while angle <= .pi {
            transCoord = coordTranAction(input, angle, rotateCoordTrans);
            print("笛卡尔坐标 \(input) 旋转\(angle)后，坐标 \(transCoord ?? [])");
            angle += .pi/6;
        }
        
        // 闭包做函数入参，尾随闭包写法
        transCoord = coordTranAction(input, Float(0.15)) {
            // 平面坐标缩放变换
            print ("$0=\($0), $1=\($1 ?? 0)");
            if $0.count < 2 {
                return nil;
            }
            guard $1 is Float else {
                return nil;
            }
            let scale:Float = $1 as! Float;
            return [$0[0]*scale, $0[1]*scale];
        }
        
        print("笛卡尔坐标 \(input) 缩放0.15后，坐标 \(transCoord ?? [])");
    }
    
    class func demoLogic() {
        sortedClosureShow();
        
        let c1 = closureAsFuncReturn(10);
        print ("call c1 return \(c1(5))");
        print ("call c1 return \(c1(5))");
        print ("call c1 return \(c1(5))");
        
        let c2 = closureAsFuncReturn(3);
        print ("call c2 return \(c2(3))");
        print ("call c2 return \(c2(3))");
        print ("call c2 return \(c2(3))");
        
        closureAsFuncInputArg();
    }
}
